#include "gui.h"
#include "GForm.h"
#include "Graphics.h"

#include <windows.h>
#define WIN_COLOR(c)                  RGB(c.red(), c.green(), c.blue())
#define WIN_RECT(gr, wr)              do{ *(&wr)=*((RECT*)&gr); wr.right++; wr.bottom++;}while(0)
 

Graphics::Graphics(GForm* pForm )
:m_pForm(pForm)
,m_hGraph(0)
,m_pPixmap(0)
{
	if (pForm != 0)
	{
		HDC hdc = 0;// ::CreateCompatibleDC(NULL);
        HWND hwnd = (HWND)pForm->getHandle();
		hdc = ::GetDC(hwnd);
		::SetTextAlign(hdc, TA_BASELINE);
		m_hGraph = hdc;
        
        setFont(GTEXT(""), 36);
//         HFONT font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
//         font = (HFONT)::SelectObject(hdc, font);
//         ::ReleaseDC(hwnd, hdc);
//         
//         ::SendMessage((HWND)pForm->getHandle(), WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
	}
}

Graphics::Graphics(const Graphics& g, GSize size /*= GSize()*/)
:m_pForm(0)
,m_hGraph(0)
,m_pPixmap(0)
{
    if (g.m_pForm != 0){
        HDC hdc = (HDC)g.m_hGraph;
        HDC memDC = ::CreateCompatibleDC(0);
        RECT rc;
        ::GetClientRect(reinterpret_cast<HWND>(g.m_pForm->getHandle()), &rc);
        HBITMAP bmp = ::CreateCompatibleBitmap(hdc, size.w, size.h);
        m_pPixmap = ::SelectObject(memDC, bmp);
        m_hGraph = memDC;
        ::SetTextAlign(memDC, TA_BASELINE);
		
        GFont::FontProperty prop;
        const GFont& font = g.getFont();
        font.getProperty(prop);
        setFont(font.getFontName(), prop.size);
    }
}
Graphics::~Graphics(void)
{
	if (m_pForm != 0)
	{
	
		::ReleaseDC((HWND)m_pForm->getHandle(), reinterpret_cast<HDC>(m_hGraph));
        m_hGraph = 0;
        m_pForm = 0;
	}
    if (m_pPixmap != 0)
    {
        HBITMAP bmp = (HBITMAP)::SelectObject(reinterpret_cast<HDC>(m_hGraph), reinterpret_cast<HBITMAP>(m_pPixmap));
        ::DeleteObject(bmp);
        ::DeleteDC(reinterpret_cast<HDC>(m_hGraph));
        m_pPixmap = 0;
        m_hGraph = 0;
    }
	m_hGraph = 0;
}

void Graphics::setPen(const PenInfo& pen)
{
// 	if (m_Pen.hPen != 0)
// 		::DeleteObject(m_Pen.hPen);
// 	HPEN hPen = (HPEN)::CreatePen(pen.style, pen.width, pen.color.toLong());
// 	HPEN hOld = (HPEN)::SelectObject((HDC)m_hGraph, hPen );
// 	m_Pen = pen;
// 	m_Pen.hPen = hPen;

}

void Graphics::setBrush(const BrushInfo& brush)
{
// 	HBRUSH hBrush = (HBRUSH)m_Brush.hBrush;
// 	if (hBrush != 0)
// 		::DeleteObject(hBrush);
// 
// 	HDC hdc = (HDC)m_hGraph;
// 	COLORREF colourNearest = ::GetNearestColor(hdc, brush.color.toLong());
// 	hBrush = (HBRUSH)::CreateSolidBrush(colourNearest);
// 	HBRUSH hOld = (HBRUSH)::SelectObject(hdc, hBrush);
// 	m_Brush = brush;
// 	m_Brush.hBrush = hBrush;
}

void Graphics::drawLine(const GPoint& start, const GPoint& end, const GColor& color)
{
	if (start == end)
		return;
	HPEN hPen = (HPEN)::CreatePen(m_Pen.style, m_Pen.width, WIN_COLOR(color)); //m_Pen.color.toLong()
	HPEN hOld = (HPEN)::SelectObject((HDC)m_hGraph, hPen );

	::MoveToEx((HDC)m_hGraph, start.x , start.y, 0);
	::LineTo((HDC)m_hGraph, end.x , end.y);

	hPen = (HPEN)::SelectObject((HDC)m_hGraph, hOld );
	::DeleteObject(hPen);
}

void Graphics::drawRect(const GRect& rc,const GColor& color)
{
// 	HDC hdc = (HDC)m_hGraph;
// 	COLORREF colourNearest = ::GetNearestColor(hdc, WIN_COLOR(color));
// 	HBRUSH hBrush = (HBRUSH)::CreateSolidBrush(colourNearest);
// 	HBRUSH hOld = (HBRUSH)::SelectObject(hdc, hBrush);
// 
// 	::Rectangle((HDC)m_hGraph, rc.left, rc.top, rc.right, rc.bottom);
// 
// 	hBrush = (HBRUSH)::SelectObject(hdc, hOld);
// 	::DeleteObject(hBrush);

	HPEN hPen = (HPEN)::CreatePen(m_Pen.style, m_Pen.width, WIN_COLOR(color)); //m_Pen.color.toLong()
	HPEN hOld = (HPEN)::SelectObject((HDC)m_hGraph, hPen );

	HDC hdc = (HDC)m_hGraph;
	::MoveToEx((HDC)m_hGraph, rc.left , rc.top, 0);
	::LineTo((HDC)m_hGraph, rc.right , rc.top);
	::LineTo((HDC)m_hGraph, rc.right , rc.bottom);
	::LineTo((HDC)m_hGraph, rc.left , rc.bottom);
	::LineTo((HDC)m_hGraph, rc.left , rc.top);

	hPen = (HPEN)::SelectObject((HDC)m_hGraph, hOld );
	::DeleteObject(hPen);
}

void Graphics::fillRect(const GRect& rc, const GColor& color)
{
	RECT rcw;
    *(&rcw)=*((RECT*)&rc);
    rcw.right++; //windows not treat right and bottom as rectangle
    rcw.bottom++;
	::SetBkColor((HDC)m_hGraph, WIN_COLOR(color));
	::ExtTextOut((HDC)m_hGraph, rc.left, rc.top, ETO_OPAQUE, &rcw, TEXT(""), 0, NULL);
}

void Graphics::drawRoundedRect(const GRect& rc, const GColor& color, const GColor& filling)
{
	HDC hdc = (HDC)m_hGraph;
	HPEN hPen = (HPEN)::CreatePen(m_Pen.style, m_Pen.width, WIN_COLOR(color)); //m_Pen.color.toLong()
	HPEN hOld = (HPEN)::SelectObject(hdc, hPen );
	::SetBkMode(hdc, TRANSPARENT);

	//COLORREF colourNearest = ::GetNearestColor(hdc, WIN_COLOR(filling));
	//HBRUSH hBrush = (HBRUSH)::CreateSolidBrush(colourNearest);
	//HBRUSH hOldBsh = (HBRUSH)::SelectObject(hdc, hBrush);
	::RoundRect(hdc,
		rc.left + 1, rc.top,
		rc.right - 1, rc.bottom,
		8, 8);
	//hBrush = (HBRUSH)::SelectObject(hdc, hOldBsh);
	//::DeleteObject(hBrush);

	hPen = (HPEN)::SelectObject((HDC)m_hGraph, hOld );
	::DeleteObject(hPen);
}

void Graphics::drawEllipse(const GRect& rc, const GColor& border, const GColor& filling)
{
    HDC hdc = (HDC)m_hGraph;
    HPEN hPen = (HPEN)::CreatePen(m_Pen.style, m_Pen.width, WIN_COLOR(border)); //m_Pen.color.toLong()
	HPEN hOld = (HPEN)::SelectObject((HDC)m_hGraph, hPen );
    HBRUSH brush = ::CreateSolidBrush(WIN_COLOR(filling));
	HBRUSH brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush));

	::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);

    brush = static_cast<HBRUSH>(::SelectObject(hdc, brushOld));
    ::DeleteObject(brush);
    hPen = (HPEN)::SelectObject((HDC)m_hGraph, hOld );
	::DeleteObject(hPen);
}

void Graphics::copy(const GRect& target, const Graphics* graph, const GRect& source /* = GRect */)
{
    ::BitBlt(static_cast<HDC>(m_hGraph), target.left, target.top, target.right,target.bottom, 
        static_cast<HDC>(graph->m_hGraph), source.left, source.top, SRCCOPY);
}

void Graphics::copy(const Graphics* graph)
{
    if (m_pForm == 0)
        return ;
    RECT rc;
    ::GetClientRect(static_cast<HWND>(m_pForm->getHandle()), &rc);
    ::BitBlt(static_cast<HDC>(m_hGraph), rc.left, rc.top, rc.right,rc.bottom, 
        static_cast<HDC>(graph->m_hGraph), 0, 0, SRCCOPY);
}
void Graphics::drawString(int x, int y, const char* str,int len ,const GColor& strColor /* = GColor */)
{
    HDC hdc = (HDC)m_hGraph;
    ::SetTextColor(hdc, WIN_COLOR(strColor));
    y += m_Font.getAscent();
    ::ExtTextOutA(hdc, x, y  ,  0, 0 , str, len, NULL);
}

void Graphics::drawClipString(const GPoint& pos, const char* str,int len,
                              const GRect& clipRect,const GColor& strColor /* = GColor */)
{
    RECT r;
    WIN_RECT(clipRect, r);
    HDC hdc = (HDC)m_hGraph;
    ::SetTextColor(hdc, WIN_COLOR(strColor));
    ::ExtTextOutA(hdc, pos.x, pos.y + m_Font.getAscent() ,  ETO_CLIPPED, &r , str, len, NULL);
}

void Graphics::drawText(int x, int y, const GString& str, 
						  const GColor& strColor)
{
 	HDC hdc = (HDC)m_hGraph;
 	::SetTextColor(hdc, WIN_COLOR(strColor));
    y += m_Font.getAscent();
    ::ExtTextOut(hdc, x, y  ,  0, 0 , str.c_str(), str.length(), NULL);
}

void Graphics::drawText(const GRect& rc, const GString& str, 
						  const GColor& strColor, const GColor& bkColor)
{
	HDC hdc = (HDC)m_hGraph;
	::SetTextColor(hdc, WIN_COLOR(strColor));
	::SetBkColor(hdc, WIN_COLOR(bkColor));

 	int h = rc.top + m_Font.getAscent();
    RECT r;
    *(&r)=*((RECT*)&rc);
    r.right++; //windows not treat right and bottom as rectangle
    r.bottom++; 

	::ExtTextOut(hdc, rc.left, h  ,  ETO_CLIPPED , &r, str.c_str(), str.length(), NULL);
}


void Graphics::setFont(const GString& fontName, int pointsize /* = 12 */)
{
    GFont::FontProperty prop;
    prop.size = (uchar)pointsize;
    m_Font.setGraphics(this);
    m_Font.setFontName(fontName);
    m_Font.setProperty(prop);
}


//////////////////////////////////////////////////////////////////////////


bool  GFont::setProperty(const FontProperty& prop)
{
    LOGFONT LogFont;
    HFONT font = 0;

    if (m_pGraphics == 0 ) return false;

    ZeroMemory(&LogFont, sizeof(LOGFONT));
    LogFont.lfStrikeOut = prop.deleteline;
    LogFont.lfUnderline = prop.underline;
    LogFont.lfHeight = prop.size;
    LogFont.lfEscapement = 0;
    LogFont.lfItalic = prop.italic;
    LogFont.lfCharSet = DEFAULT_CHARSET;
    if (!m_FontName.empty())
        lstrcpy(LogFont.lfFaceName, m_FontName.c_str());
    LONG w = FW_NORMAL;
    switch(prop.bold)
    {
    case FontProperty::Normal: w = FW_NORMAL; break;
    case FontProperty::Thin:   w = FW_LIGHT; break;
    case FontProperty::Heavy:  w = FW_BOLD; break;
    }
    LogFont.lfWeight = (short)w;

    HDC hDC = (HDC)m_pGraphics->getHandle();
    if (!m_FontName.empty())
    {
        font = ::CreateFontIndirect(&LogFont);
    }
    else
    {
        font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
    }
    if (font == NULL)  return false;
    m_hFont = font;
    font = (HFONT)::SelectObject(hDC, font);
    ::SetBkMode(hDC, TRANSPARENT);
    TEXTMETRIC tm;
    ::GetTextMetrics(hDC, &tm);
    m_FontHeight = (short)(tm.tmHeight + tm.tmExternalLeading);
    m_FontWidth = (short)(tm.tmAveCharWidth);
    m_Ascent = (short)(tm.tmAscent);
    m_Decent = (short)(tm.tmDescent);
    ::DeleteObject(font);
    
    return true;
}
void  GFont::getProperty(FontProperty& prop)const
{
    TEXTMETRIC tm;
    HDC hDC = (HDC)m_pGraphics->getHandle();
    ::GetTextMetrics(hDC, &tm);
    prop.italic = tm.tmItalic;
    prop.deleteline = tm.tmStruckOut;
    prop.underline = tm.tmUnderlined;
    prop.size = (uchar)tm.tmHeight;
    FontProperty::Weight w = FontProperty::Normal;
    switch(tm.tmWeight)
    {
    case FW_NORMAL:   w = FontProperty::Normal; break;
    case FW_LIGHT:    w = FontProperty::Thin; break;
    case FW_BOLD:     w = FontProperty::Heavy; break;
    }
    prop.bold = w;
}

GSize GFont::getTextSize(const GString& text) const
{
    SIZE sz;
    ::GetTextExtentPoint32((HDC)m_pGraphics->getHandle(), text.c_str(), text.length(), &sz);
    return GSize(sz.cx, sz.cy);
}

GSize GFont::getStringSize(const char* pstr, int len) const
{
    SIZE sz;
    ::GetTextExtentPoint32A((HDC)m_pGraphics->getHandle(), pstr, len, &sz);
    return GSize(sz.cx, sz.cy);
}